package com.projectbox.web;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

import com.projectbox.model.Project;
import com.projectbox.model.Resource;
import com.projectbox.model.User;
import com.projectbox.model.Folder;
import com.projectbox.service.ProjectService;
import com.projectbox.service.ResourceService;
import com.projectbox.service.UserService;

@Controller
@RequestMapping("/project")
public class ProjectFolderController extends MultiActionController {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private UserService userService;
	private ProjectService projectService;
	private ResourceService resourceService;

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public void setProjectService(ProjectService projectService) {
		this.projectService = projectService;
	}

	public void setResourceService(ResourceService resourceService) {
		this.resourceService = resourceService;
	}
  /**
   * Display other includ9ng public and private projects
   *
   */
	@RequestMapping(method = RequestMethod.GET)
	public String displayOtherProjects(Model model) {

		logger.debug("display other public projects");
		User user = userService.getCurrentUser();
		List<Project> projectList = projectService.findNonContributedProjects(user);
		
		model.addAttribute("user", user);
		model.addAttribute("projectList", projectList);
		
		return "otherProjects";
	}
   
	/**
   * Only display public projects
   *
   */
	@RequestMapping(value = "/pub", method = RequestMethod.GET)
	public String displayOtherPublicProjects(Model model) {

		logger.debug("display other public projects");
		User user = userService.getCurrentUser();
		List<Project> publicProjectList = projectService.findNonContributedProjects(user, false);
		
		model.addAttribute("ptype", "Public");
		model.addAttribute("user", user);
		model.addAttribute("projectList", publicProjectList);
		
		return "otherProjects";
	}

	/**
   * Only display private projects
   *
   */	
	@RequestMapping(value = "/prv", method = RequestMethod.GET)
	public String displayOtherPrivateProjects(Model model) {

		logger.debug("display other public projects");
		User user = userService.getCurrentUser();
		List<Project> privateProjectList = projectService.findNonContributedProjects(user, true);
		
		model.addAttribute("ptype", "Private");
		model.addAttribute("user", user);
		model.addAttribute("projectList", privateProjectList);
		
		return "otherProjects";
	}
 
	/**
   * Go to the page showing this contribuer's projects
   */  
	@RequestMapping(value = "/request", method = RequestMethod.GET)
	public String processProjectParticipateRequest(
			@RequestParam(value = "pid", required = true) Long pid) {

		logger.debug("request project with pid " + pid);

		User user = userService.getCurrentUser();
		Project project = projectService.findProject(pid);
		
		if (user != null && project != null) {
			projectService.processProjectContributorRequest(project, user);
		} else {
			logger.debug("something is wrong on current user and project");
			return "redirect:/error/403.jsp";
		}
		if (project.getPrivacy()) {
			return "redirect:/project/prv";
		} else {
			return "redirect:/contribute";
		}
	}
	

	/**
   * Download resources under a project
   */
	@RequestMapping(value = "/{pid}/download", method = RequestMethod.GET)
	public void processDownloadProjectResources(@PathVariable("pid") Long pid,
			@RequestParam(value = "rid", required = true) Long rid,
			HttpServletResponse response) throws IOException {

		logger.debug("download resource");
		logger.debug("the pid " + pid);
		logger.debug("the rid " + rid);

		Project project = projectService.findProject(pid);
		if (project == null) {	
			response.setStatus(400);
			return;
		}
		
		String status = projectService.getProjectStatus(userService.getCurrentUser().getId(), project);
		logger.debug(status);
		if (!status.equals("")) {
			if (!status.equals("granted")) {
				logger.debug("not granted");
				response.setStatus(403);
				return;
			}
		} else {
			if (project.getMid() != userService.getCurrentUser().getId()) {
				logger.debug("not PM");
				
				response.setStatus(403);
				return;
			}
		}
		
		Resource resource = this.resourceService.findResource(rid);
		if (resource == null) {
			response.setStatus(400);
			return;
		}
		
		if (resource.getPid() != pid) {
			response.setStatus(404);
			return;
		}
		
		response.setContentType(resource.getType());
		response.setContentLength(resource.getFile().length);
		response.setHeader("Content-Disposition","attachment; filename=\""
        						+ resource.getName() +"\"");
		FileCopyUtils.copy(resource.getFile(), response.getOutputStream());
	}
 

	/**
   * Delete a resource from a project
   */ 
	@RequestMapping(value = "/{pid}/delete", method = RequestMethod.GET)
	public String processDeleteProjectResources(@PathVariable("pid") Long pid,
			@RequestParam(value = "rid", required = true) Long rid,
			HttpServletResponse response) throws IOException {
		
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/403.jsp";
		}
		
		String status = projectService.getProjectStatus(userService.getCurrentUser().getId(), project);
		logger.debug(status);
		if (!status.equals("")) {
			if (!status.equals("granted")) {
				logger.debug("not granted");
				return "redirect:/error/403.jsp";
			}
		} else {
			if (project.getMid() != userService.getCurrentUser().getId()) {
				logger.debug("not PM");
				return "redirect:/error/403.jsp";
			}
		}
		
		Resource resource = this.resourceService.findResource(rid);
		if (resource == null) {
			return "redirect:/error/400.jsp";
		}
		
		this.resourceService.removeResource(resource);
	
		return "redirect:/project/" + pid;
	}	
 

	/**
   * Delete a reaource from a specifed folder
   */ 
	@RequestMapping(value = "/{pid}/{fid}/delete", method = RequestMethod.GET)
	public String processDeleteResourcesInFolder(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid,
			@RequestParam(value = "rid", required = true) Long rid,
			HttpServletResponse response) throws IOException {
		
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		String status = projectService.getProjectStatus(userService.getCurrentUser().getId(), project);
		logger.debug(status);
		if (!status.equals("")) {
			if (!status.equals("granted")) {
				logger.debug("not granted");
				return "redirect:/error/403.jsp";
			}
		} else {
			if (project.getMid() != userService.getCurrentUser().getId()) {
				logger.debug("not PM");
				return "redirect:/error/403.jsp";
			}
		}
		
		if (this.resourceService.findFolder(fid) == null) {
			return "redirect:/error/400.jsp";
		}
		
		Resource resource = this.resourceService.findResource(rid);
		if (resource == null) {
			return "redirect:/error/400.jsp";
		}
		
		this.resourceService.removeResource(resource);
	
		return "redirect:/project/" + pid + "/" + fid;
	}	
   
	/**
   * Upload a reaource to a specifed folder
   */ 
	@RequestMapping(value = "/{pid}/upload", method = RequestMethod.POST)
	public String processUploadProjectResources(@PathVariable("pid") Long pid,
			HttpServletRequest request) throws Exception {

		logger.debug("the pid " + pid);

		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		MultipartFile multipartFile = multipartRequest.getFile("file");
		
		String name = multipartFile.getOriginalFilename();
		String description = ServletRequestUtils.getStringParameter(request, "desc");
		Long uid = this.userService.getCurrentUser().getId();
		byte[] content = multipartFile.getBytes(); // byte[]
		
		logger.debug("before add Resource");
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		this.resourceService.addResource(content, name, description, uid, pid, (long)0);

		return "redirect:/project/" + pid;
	}
   
 	/**
   * Upload  a reaource to  a specifed folder
   */   
	@RequestMapping(value = "/{pid}/{fid}/upload", method = RequestMethod.POST)
	public String processUploadResourcesInFolder(@PathVariable("pid") Long pid,@PathVariable("fid") Long fid,
			HttpServletRequest request) throws Exception {

		logger.debug("processUploadResourcesInFolder pid " + pid + "fid " + fid);

		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		MultipartFile multipartFile = multipartRequest.getFile("file");
		
		String name = multipartFile.getOriginalFilename();
		String description = ServletRequestUtils.getStringParameter(request, "desc");
		//String type = multipartFile.getContentType();
		Long uid = this.userService.getCurrentUser().getId();
		byte[] content = multipartFile.getBytes(); // byte[]
		
		logger.debug("before add Resource");
		
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		if (this.resourceService.findFolder(fid) == null) {
			return "redirect:/error/400.jsp";
		}
		
		this.resourceService.addResource(content, name, description, uid, pid, fid);

		return "redirect:/project/" + pid + "/" + fid;
	}

	/**
   * Display reaource under a specifed project
   */ 
	@RequestMapping(value = "/{pid}", method = RequestMethod.GET)
	public String displayProjectResources(@PathVariable Long pid, Model model) {

		logger.debug("view resource for project with id " + pid);

		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		User user = userService.getCurrentUser();
		
		if (project.getMid() != user.getId()) {
			if (!projectService.getProjectStatus(user.getId(), project).equals("granted")) {
				return "redirect:/error/403.jsp";
			}
		}
		
		List<Folder> folderList = resourceService.findSubfoldersByPid(pid);
		List<Resource> resourceList = resourceService.findProjectResources(pid);

		logger.debug("project" + project);
		logger.debug("foderList" + folderList);
		logger.debug("resourceList" + resourceList);		

		model.addAttribute("user", user);
		model.addAttribute("project", project);
		model.addAttribute("folders", folderList);
		model.addAttribute("resources", resourceList);

		return "resource";
	}
 
	/**
   * Display reaources under a specifed folder
   */  
	@RequestMapping(value = "/{pid}/{fid}", method = RequestMethod.GET)
	public String displayResourceInFolder(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid, Model model) {
		
		logger.debug("view resource in folder " + fid + " pid " + pid);

		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		User user = userService.getCurrentUser();
		if (project.getMid() != user.getId()) {
			if (!projectService.getProjectStatus(user.getId(), project).equals("granted")) {
				return "redirect:/error/403.jsp";
			}
		}
		
		Folder folder = resourceService.findFolder(fid);
		if (folder == null) {
			return "redirect:/error/400.jsp";
		}
		
		List<Folder> folderList = resourceService.findSubfolders(fid);		
		List<Resource> resourceList = resourceService.findResourcesByFolder(fid);
		List<Folder> traceFolders = resourceService.findPreFoldersById(fid);		
		
		logger.debug("" + project);
		logger.debug("" + folder.getName());
		logger.debug("fid" + folder.getId());
		logger.debug("" + folderList);
		logger.debug("" + resourceList);
		logger.debug("trace" + traceFolders);
	
		model.addAttribute("user", user);
		model.addAttribute("project", project);
		model.addAttribute("folders", folderList);
		model.addAttribute("resources", resourceList);		
		model.addAttribute("folder", folder);
		model.addAttribute("traceFolders", traceFolders);
		
		return "resource";
	}

	/**
   * Create a folder in a project
   */ 
	@RequestMapping(value = "/{pid}/createFolder", method = RequestMethod.POST)
	public String processCreateFolder(@PathVariable("pid") Long pid,
			@RequestParam(value = "folderName", required = true) String folderName,
			HttpServletResponse response, Model model) throws Exception {
		logger.debug("inside pid" + pid + folderName);

		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		resourceService.createNewFolder(folderName, (long)0, pid);		
		
		List<Folder> folderList = resourceService.findSubfoldersByPid(pid);
		List<Resource> resourceList = resourceService.findProjectResources(pid);

		logger.debug("project" + project);
		logger.debug("foderList" + folderList);
		logger.debug("resourceList" + resourceList);
		logger.debug("length" + folderList.size());

		model.addAttribute("user", userService.getCurrentUser());
		model.addAttribute("project", project);
		model.addAttribute("folders", folderList);
		model.addAttribute("resources", resourceList);
		model.addAttribute("fListLength", "" + (folderList.size()-1));
		
		return "resource";
	}
 
	/**
   * Create a folder in a sub folder
   */  
	@RequestMapping(value = "/{pid}/{fid}/createFolder", method = RequestMethod.POST)
	public String processCreateSubfolder(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid,
			@RequestParam(value = "folderName", required = true) String folderName,
			HttpServletResponse response) throws Exception {
		
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		if (this.resourceService.findFolder(fid) == null) {
			return "redirect:/error/400.jsp";
		}
		
		resourceService.createNewFolder(folderName, fid, pid);
		return "redirect:/project/" + pid + "/" + fid;
		
	}
 
	/**
   * Deleate a folder from a project
   */  
	@RequestMapping(value = "/{pid}/{fid}/deleteFolder", method = RequestMethod.GET)
	public String processDeleteFolder(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid) throws IOException {
		Project project = projectService.findProject(pid);
		if (project == null) {
			return "redirect:/error/400.jsp";
		}
		
		if (this.resourceService.findFolder(fid) == null) {
			return "redirect:/error/400.jsp";
		}
		
		Folder root = resourceService.findFolder(fid);
		this.resourceService.deleteFolder(fid);
		if(root.getPreId()==0)
			return "redirect:/project/" + pid;
		else 
			return "redirect:/project/" + pid + "/" + root.getPreId();
	}	
 

	/**
   * Rename a folder
   */   
	@RequestMapping(value = "/{pid}/{fid}/renameFolder", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, String> processRenameFolder(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid,
			@RequestParam(value = "fname", required = true) String newName,
			HttpServletResponse response) throws IOException {
	
		Map<String, String> rnames = this.resourceService.renameFolder(fid, newName);
		logger.debug("fid " + fid + " newName " + newName);
		return rnames;
	}


	/**
   * Rename a resource in a specified project
   */  
	@RequestMapping(value = "/{pid}/renameResource", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, String> processRenameResourcesInFolder(@PathVariable("pid") Long pid,
			@RequestParam(value = "rid", required = true) Long rid, @RequestParam(value = "rname", required = true) String newName,
			HttpServletResponse response) throws IOException {
		
		Map<String, String> fnames = this.resourceService.renameResource(rid, newName);
		logger.debug("rid" + rid);
		
		return fnames;
	}
 

	/**
   * Get a suggested folder name if this name has already existed
   */   
	@RequestMapping(value = "/{pid}/{fid}/getSuggestFolderName", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, String> getSuggestFolderName(@PathVariable("pid") Long pid, @PathVariable("fid") Long fid,
			@RequestParam(value = "initialName", required = true) String initialName,
			HttpServletResponse response) throws IOException {		
		Map<String, String> suggestName = new HashMap<String, String>();
		String temp = this.resourceService.getInitialFolderName(initialName, fid);
		suggestName.put("suggestName", temp);
		logger.debug("fid" + fid);
		
		return suggestName;
	}

}
